package util.biz.excel;

import util.util.DevComUtil;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.*;
import java.util.Map.Entry;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * 校验--excel--配置信息
 *
 * @author: liW
 * @date: 2020/11/28 13:40
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ExcelCheckConfig<T> {


  /**
   * 操作的数据
   */
  private List<RowData<T>> rowDataList;

  /**
   * 读取配置
   */
  private ExcelConverConfig<T> config;
  /**
   * 数据库中所有的数据
   */
  private List<T> dbList;

  /**
   * 导入条数
   */
  private int insertCount = 0;

  /**
   * 获取数据库中数据的方法
   */
  private Supplier<List<T>> dataSupplier;

  /**
   * 行信息
   *
   * @param <T> 类型
   */
  @Data
  @NoArgsConstructor
  @AllArgsConstructor
  public static class RowData<T> {

    /**
     * 读取的行数据
     */
    private T data;

    /**
     * 行号
     */
    private int rowNo;

    /**
     * 是否插入数据库
     */
    private boolean insertFlag;

    /**
     * excel重复收集
     */
    private List<Msg> excelRepeat;

    /**
     * 数据库重复收集
     */
    private List<Msg> dbRepeat;

    /**
     * 超长收集
     */
    private List<Msg> overLength;

    /**
     * 必填为空收集
     */
    private List<String> mustList;

    /**
     * 构造
     *
     * @param rowNo 行号
     * @param data 数据
     * @param <T> 类型
     * @return 行信息
     */
    public static <T> RowData<T> getInstance(int rowNo, T data) {
      RowData<T> rowData = new RowData<>();
      rowData.setRowNo(rowNo);
      rowData.setData(data);
      rowData.setInsertFlag(true);
      return rowData;
    }
  }

  /**
   * 提示信息
   */
  @Data
  @NoArgsConstructor
  @AllArgsConstructor
  public static class Msg {

    /**
     * 字段名称
     */
    private String name;

    /**
     * 字段值
     */
    private Object value;

    /**
     * 长度
     */
    private int length;

  }

  /**
   * 长度校验
   *
   * @param <T>
   */
  @Data
  @NoArgsConstructor
  @AllArgsConstructor
  public static class CheckLength<T> {

    /**
     * 获取值
     */
    private Function<T, Object> check;

    /**
     * 规定的长度
     */
    private int length;

  }


  /**
   * 获取可以导入的数据
   *
   * @return 可以导入的数据
   */
  public List<T> getInsertList() {
    if (DevComUtil.isEmpty(this.rowDataList)) {
      return Collections.emptyList();
    }
    return this.rowDataList.stream().filter(RowData::isInsertFlag).map(RowData::getData)
        .collect(Collectors.toList());
  }

  /**
   * 校验--必填
   *
   * @return 配置信息
   */
  public ExcelCheckConfig<T> checkMust() {
    List<RowData<T>> rowDataList = this.getRowDataList();
    if (DevComUtil.isEmpty(rowDataList)) {
      return this;
    }
    Map<String, Function<T, Object>> map = this.getConfig().getList().stream()
        .filter(item -> item.getMaxLength() > 0)
        .collect(Collectors.toMap(item -> item.getTitle().replace("*", ""), ExcelConverConfig.ExcelCell::getFun));

    for (Entry<String, Function<T, Object>> entry : map.entrySet()) {
      Function<T, Object> f = entry.getValue();

      // 筛选出导入的字段是否存在此值
      List<RowData<T>> repeat = rowDataList.stream()
          .filter(item -> DevComUtil.isEmpty(f.apply(item.getData())))
          .collect(Collectors.toList());
      if (!DevComUtil.isEmpty(repeat)) {
        for (RowData<T> rowData : repeat) {
          rowData.setInsertFlag(false);
          List<String> mustList = rowData.getMustList();
          if (mustList == null) {
            mustList = new ArrayList<>();
          }
          mustList.add(entry.getKey());
          rowData.setMustList(mustList);
        }
      }
    }
    return this;
  }

  /**
   * 校验--长度
   *
   * @return 配置信息
   */
  public ExcelCheckConfig<T> checkLength() {
    List<RowData<T>> rowDataList = this.getRowDataList();
    if (DevComUtil.isEmpty(rowDataList)) {
      return this;
    }
    Map<String, CheckLength<T>> map = this.getConfig().getList().stream()
        .filter(item -> item.getMaxLength() > 0)
        .collect(Collectors.toMap(item -> item.getTitle().replace("*", ""),
            item -> new CheckLength<T>(item.getFun(), item.getMaxLength())));

    for (Entry<String, CheckLength<T>> entry : map.entrySet()) {
      CheckLength<T> value = entry.getValue();

      for (RowData<T> rowData : rowDataList) {
        T data = rowData.getData();
        Object apply = value.getCheck().apply(data);

        if (apply != null && apply.toString().length() > value.getLength()) {
          rowData.setInsertFlag(false);
          Msg msg = new Msg();
          msg.setName(entry.getKey());
          msg.setValue(apply);
          msg.setLength(value.getLength());
          List<Msg> overLength = rowData.getOverLength();
          if (overLength == null) {
            overLength = new ArrayList<>();
          }
          overLength.add(msg);
          rowData.setOverLength(overLength);
        }
      }
    }
    return this;
  }


  /**
   * 校验--Excel 重复数据
   *
   * @return repeatConfig 重复数据配置
   */
  public ExcelCheckConfig<T> checkExcelRepeat() {
    List<RowData<T>> rowDataList = this.getRowDataList();
    if (DevComUtil.isEmpty(rowDataList)) {
      return this;
    }

    Map<String, Function<T, Object>> checkExcelMap = this.getConfig().getList().stream()
        .filter(ExcelConverConfig.ExcelCell::isExcelRepeat)
        .collect(Collectors.toMap(item -> item.getTitle().replace("*", ""), ExcelConverConfig.ExcelCell::getFun));

    if (DevComUtil.isEmpty(checkExcelMap)) {
      return this;
    }

    for (Entry<String, Function<T, Object>> entry : checkExcelMap.entrySet()) {
      // 根据判断的字段分组
      Map<Object, List<RowData<T>>> map = this.getRowDataList().stream().collect(
          Collectors
              .groupingBy(item -> entry.getValue().apply(item.getData()) == null ? "" : entry.getValue().apply(item.getData()), Collectors.toList()));

      for (Entry<Object, List<RowData<T>>> listEntry : map.entrySet()) {
        List<RowData<T>> value = listEntry.getValue();
        // 若  > 1 说明此条数据重复了。则记录
        if (value.size() > 1) {
          for (RowData<T> rowData : value) {
            rowData.setInsertFlag(false);
            Msg msg = new Msg();
            msg.setName(entry.getKey());
            msg.setValue(entry.getValue().apply(rowData.getData()));
            List<Msg> excelRepeat = rowData.getExcelRepeat();
            if (excelRepeat == null) {
              excelRepeat = new ArrayList<>();
            }
            excelRepeat.add(msg);
            rowData.setExcelRepeat(excelRepeat);
          }
        }
      }
    }
    return this;
  }

  /**
   * 校验--数据库 重复数据
   *
   * @return repeatConfig 重复数据配置
   */
  public ExcelCheckConfig<T> checkDbRepeat() {

    List<RowData<T>> rowDataList = this.getRowDataList();
    if (DevComUtil.isEmpty(rowDataList)) {
      return this;
    }

    if (this.dataSupplier != null) {
      this.dbList = this.dataSupplier.get();
    }

    if (DevComUtil.isEmpty(this.dbList)) {
      return this;
    }

    List<ExcelConverConfig.ExcelCell> list = this.getConfig().getList();
    Map<String, Function<T, Object>> checkMap = list.stream().filter(ExcelConverConfig.ExcelCell::isDbRepeat)
        .collect(Collectors.toMap(item -> item.getTitle().replace("*", ""), ExcelConverConfig.ExcelCell::getFun));
    if (DevComUtil.isEmpty(checkMap)) {
      return this;
    }

    for (Entry<String, Function<T, Object>> entry : checkMap.entrySet()) {

      Function<T, Object> f = entry.getValue();
      // 从数据库中 提取出需要判断的字段值集合
      Set<Object> dbSet = this.dbList.stream().map(f).collect(Collectors.toSet());

      // 筛选出导入的字段是否存在此值
      List<RowData<T>> repeat = rowDataList.stream()
          .filter(item -> dbSet.contains(f.apply(item.getData())))
          .collect(Collectors.toList());

      // 若存在，则记录
      if (!DevComUtil.isEmpty(repeat)) {
        for (RowData<T> rowData : repeat) {
          rowData.setInsertFlag(false);
          Msg msg = new Msg();
          msg.setName(entry.getKey());
          msg.setValue(entry.getValue().apply(rowData.getData()));
          List<Msg> dbRepeat = rowData.getDbRepeat();
          if (dbRepeat == null) {
            dbRepeat = new ArrayList<>();
          }
          dbRepeat.add(msg);
          rowData.setDbRepeat(dbRepeat);
        }
      }
    }
    return this;
  }

  /**
   * 拼接提示语句
   *
   * @return 提示语句
   */
  public String toMsg() {

    StringBuilder msg = new StringBuilder();

    for (RowData<T> rowData : this.rowDataList) {
      if (!rowData.insertFlag) {
        int rowNo = rowData.getRowNo();
        List<Msg> excelRepeat = rowData.getExcelRepeat();
        List<Msg> dbRepeat = rowData.getDbRepeat();
        List<Msg> overLength = rowData.getOverLength();
        msg.append("第");
        msg.append(rowNo);
        msg.append("行：");
        // 第一行： excel重复 电话号码（重复号码），姓名（重复名称）；数据超长 电话号码[6]（超长号码）；
        toStr(msg, excelRepeat, "Excel导入重复 ");
        toStr(msg, dbRepeat, "数据库中存在 ");
        toStr(msg, overLength, "数据超长 ");
        List<String> mustList = rowData.getMustList();
        if (!DevComUtil.isEmpty(mustList)) {
          msg.append("必填字段 ");
          msg.append(String.join("，", mustList));
        }
        msg.append("\n");
      }
    }
    return msg.toString();
  }

  /**
   * 每一行拼接
   *
   * @param msg 提示语句拼接stringbuilder
   * @param list 需要提示的字段
   * @param title 类型名称
   */
  private static void toStr(StringBuilder msg, List<Msg> list, String title) {
    if (!DevComUtil.isEmpty(list)) {
      msg.append(title);
      StringBuilder item = new StringBuilder();
      for (Msg m : list) {
        item.append(m.getName());

        if (m.getLength() > 0) {
          item.append("[");
          item.append(m.length);
          item.append("]");
        }
        item.append("（");
        item.append(m.getValue());
        item.append("）");
        item.append(",");
      }
      if (item.length() > 0) {
        msg.append(item.substring(0, item.length() - 1));
        msg.append("；");
      }
    }
  }


}
